//1. null和undefined 是所有类型的子类型，可以赋值给他们
let num: number = undefined;

let u: undefined;
let num2: number = u;

//2. void 空值
//在ts中，可以用void表示没有任何返回值的函数
//声明一个void类型的变量没有什么用，因为你只能将它赋值为undefined和null
function alertName(): void {}

let unusable: void = undefined;

//3. 联合类型
//未赋值前，只能访问此联合类型的所有类型里共有的属性或方法：
function getLength(sth: string | number): number {
  // return sth.length;

  return;
}
function getLength2(sth: string | number): string {
  return sth.toString();
}

//赋值后，会根据类型推论的规则推断出一个类型
let myFavoriteNumber: string | number;
myFavoriteNumber = "seven";
console.log(myFavoriteNumber.length);

//4. interface 接口
interface Person {
  name: string;
  age?: number; //？代表可选属性，该属性可以不存在
}

//5. 数组 的多种定义方式
let fibonacci: number[] = [1, 1, 2, 3, 5]; //类型+方括号

let fibonacci2: Array<number> = [1, 1, 2, 3, 5]; //数组泛型(Array Generic) Array<elemType>

interface NumberArray {
  [index: number]: number; //接口也可用来描述数组。但常用于表示类数组
}

//类数组(Array-like Object)
function sum() {
  // let args: number[] = arguments;  error
}
function sum2() {
  //arguments 实际上是一个类数组，不能用普通的数组的方式来描述，而应该用接口
  let args: {
    [index: number]: any;
    length: number;
    callee: Function;
  } = arguments;
}
//事实上常用的类数组都有自己的接口定义，如 IArguments,NodeList,HTMLCollection等等
function sum3(): void {
  let args: IArguments = arguments;
}

//6. 函数的类型
// 一个函数有输入和输出，要在 TypeScript 中对其进行约束，需要把输入和输出都考虑到
// 第一种，函数声明 ，类型定义较简单
function add(x: number, y: number): number {
  return x + y;
}
// 注意：输入多余的（或者少于要求的）参数，是不被允许的

//第二种，函数表达式
//在 TypeScript 的类型定义中，=> 用来表示函数的定义，左边是输入类型，需要用括号括起来，右边是输出类型。
let mySum: (x: number, y: number) => number = function (
  x: number,
  y: number
): number {
  return x + y;
};

//接口定义函数的形状
interface SearchFunc {
  (source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
mySearch = function (source: string, subString: string) {
  return source.search(subString) !== -1;
};

//** 可选参数必须接在必需参数后面
function buildName1(firstName: string, lastName?: string) {}

//参数默认值，TS会将添加了默认值的参数识别为可选参数，但不必放在必选参数后面
function buildName(firstName: string = "Tom", lastName: string): string {
  return firstName + "" + lastName;
}

//剩余参数 (rest参数)
function push(num: any[], ...items: any[]) {
  items.forEach(function (item) {
    num.push(item);
  });
}

//重载 --允许一个函数接受不同数量或类型的参数时，作出不同的处理
function reverse2(x: number | string): number | string | void {
  if (typeof x === "number") {
    return Number(x.toString().split("").reverse().join(""));
  } else if (typeof x === "string") {
    return x.split("").reverse().join("");
  }
} //利用联合类型实现时，有一个缺点，不能够精确地表达，输入和输出为同一个类型

//利用重载定义多个reverse的函数类型
function reverse(x: number): number;
function reverse(x: string): string;
function reverse(x: number | string): number | string | void {
  if (typeof x === "number") {
    return Number(x.toString().split("").reverse().join(""));
  } else if (typeof x === "string") {
    return x.split("").reverse().join("");
  }
}

//*注意： ts会优先从最前面的函数定义开始匹配，所以多个函数定义如果有包含关系，需要优先把精确地定义写在前面

//自动编译.ts文件
//创建tsconfig.json 文件 （tsc--init生成配置文件）
// 终端，运行任务 tsc：监视
